package com.ifd.iview.as400.common;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.sql.DataSource;

import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.util.JdbcUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ibm.as400.access.AS400;
import com.ibm.as400.access.AS400JDBCDataSource;

public class JdbcRealmAs400 extends JdbcRealm {
	protected static final String DEFAULT_AUTHENTICATION_QUERY = 
			"SELECT uppaswrd FROM ivusrprfp WHERE upusrnam = ?";
	protected static final String DEFAULT_USER_ROLES_QUERY = 
			"";
	protected String authenticationQuery = DEFAULT_AUTHENTICATION_QUERY;  
    protected String userRolesQuery = DEFAULT_USER_ROLES_QUERY;
    protected boolean permissionsLookupEnabled = false;
    protected DataSource dataSource = null;
    
//    private static final Logger log = LoggerFactory.getLogger(JdbcRealmAs400.class);
	public JdbcRealmAs400() {
		super();
		setAuthenticationQuery(authenticationQuery);
		setUserRolesQuery(userRolesQuery);
	}
	
	@Override
	public void setAuthenticationQuery(String authenticationQuery) {
		this.authenticationQuery = authenticationQuery;
		super.setAuthenticationQuery(this.authenticationQuery);
	}
	
	@Override
	public void setUserRolesQuery(String userRolesQuery) {
		this.userRolesQuery = userRolesQuery;
		super.setUserRolesQuery(this.userRolesQuery);
	}
	
	@Override
	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
		super.setDataSource(dataSource);
	}
	
	@Override
	/**
	 * Accept token is instance of class UsernamePasswordToken
	 */
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		UsernamePasswordToken upToken = (UsernamePasswordToken)token;
		String username = upToken.getUsername().toLowerCase().trim();
		if(username == null)
			throw new AccountException("Null usernames are not allowed by this realm.");
		
		Connection conn = null;
		AuthenticationInfo info = null;
		try {
			conn = dataSource.getConnection();
			String password = getPasswordForUser(conn, username);
			if (password == null)
                throw new UnknownAccountException("No account found for user [" + username + "]");
            info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName());
		} catch (Exception e) {
			final String message = "There was a SQL error while authenticating user [" + username + "]";  
//			log.error(message, e);
			throw new AuthenticationException(message, e);
		} finally {
			JdbcUtils.closeConnection(conn);
		}
		return info;
	}
	
	private String getPasswordForUser(Connection conn, String username) throws SQLException{
        PreparedStatement ps = null;  
        ResultSet rs = null;  
        String password = null;  
        try {  
            ps = conn.prepareStatement(authenticationQuery);  
            ps.setString(1, username);  
  
            // Execute query  
            rs = ps.executeQuery();  
            // Loop over results - although we are only expecting one result,  
            // since usernames should be unique  
            boolean foundResult = false;  
            while (rs.next()) {  
                // Check to ensure only one row is processed  
                if (foundResult) {  
                    throw new AuthenticationException(  
                            "More than one user row found for user ["  
                                    + username + "]. Usernames must be unique.");  
                }  
                password = rs.getString(1).trim();  
                foundResult = true;  
            }  
        } finally {  
            JdbcUtils.closeResultSet(rs);  
            JdbcUtils.closeStatement(ps);  
        }  
  
        return password;  
	}
	
    protected Set getRoleNamesForUser(Connection conn, String username) throws SQLException {  
        PreparedStatement ps = null;  
        ResultSet rs = null;  
        Set roleNames = new LinkedHashSet();  
  
        try {  
            ps = conn.prepareStatement(userRolesQuery);  
            ps.setString(1, username);  
            ps.setString(2, username);  
  
            // Execute query  
            rs = ps.executeQuery();  
 
            // Loop over results and add each returned role to a set  
            while (rs.next()) {  
  
                String roleName = rs.getString(1);  
  
                // Add the role to the list of names if it isn't null  
                if (roleName != null) {  
                    roleNames.add(roleName);  
                } else {  
//                    if (log.isWarnEnabled()) {  
//                        log.warn("Null role name found while retrieving role names for user [" + username + "]");  
//                    }  
                }  
            }  
        } finally {  
            JdbcUtils.closeResultSet(rs);  
            JdbcUtils.closeStatement(ps);  
        }
        return roleNames;  
    } 
}
